package bayaba.engine.bow;

import java.util.Random;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import bayaba.engine.lib.*;

public class GameMain
{
	private static int MAX_PATTERN = 30; 
	private static int MAX_ARROW = 300; 
	
	public GL10 gl = null;
	public boolean Pause = false, GameStart = false;
	
	private Context MainContext;
	private Random MyRand = new Random();
	private int SndBuff[] = new int [10];
	
	public GameInfo gInfo;	
	private Sprite Pattern[] = new Sprite[MAX_PATTERN];
	
	private GameObject Back[] = new GameObject[20]; // 배경을 위한 오브젝트 (산, 나무 ) 
	private GameObject Player1[] = new GameObject[2]; // 플레이어1을 위한 오브젝트 0번은 바디, 1번은 팔부분
	private GameObject Player2[] = new GameObject[2]; // 플레이어2을 위한 오브젝트 0번은 바디, 1번은 팔부분
	private GameObject Arrow[] = new GameObject[MAX_ARROW]; // 화살과 점선,피터짐을 위한 오브젝트 type이 0인 것은 화살, 1번은 점선, 2번은 피터짐
	
	private int TotalBack = 0; // 전체 배경 오브젝트의 개수
	
	public float TouchX, TouchY; // 실제로 게임 화면 상에 터치된 좌표  
	public float StartX, StartY, TargetX, TargetY; // 터치 시작 좌표와 끝 좌표
	public boolean TouchFlag = false; // 터치 체크용 플래그 변수
	
	
		
	public GameMain( Context context, GameInfo info )
	{
		int i;
		
		MainContext = context;
		gInfo = info;
		
		// 배열로 선언된 각 스프라이트와 오브젝트들을 초기화 한다.
		
		for ( i = 0; i < Pattern.length; i++ ) Pattern[i] = new Sprite();
		for ( i = 0; i < Back.length; i++ ) Back[i] = new GameObject();
		for ( i = 0; i < Arrow.length; i++ ) Arrow[i] = new GameObject();
		
		for ( i = 0; i < Player1.length; i++ ) Player1[i] = new GameObject();
		for ( i = 0; i < Player2.length; i++ ) Player2[i] = new GameObject();
		
		SndBuff[0] = gInfo.SndPool.load( MainContext, R.raw.arrow, 0 );
		SndBuff[1] = gInfo.SndPool.load( MainContext, R.raw.arrowhit, 0 );
		SndBuff[2] = gInfo.SndPool.load( MainContext, R.raw.arrowmiss, 0 );
	}

	public void LoadData()
	{
		int i;
		
		// 게임에서 사용될 스프라이트 파일 2개를 각각 로딩한다.
		Pattern[0].LoadSprite( gl, MainContext, "back.spr" );
		Pattern[1].LoadSprite( gl, MainContext, "ground.spr" );
		
		// 산 오브젝트 3개를 우측으로 나열시켜 생성한다.
		for ( i = 0; i < 3; i++ )
		{
			Back[TotalBack].SetObject( Pattern[1], 0, 0, i * 748, 240, 0, 0 );
			Back[TotalBack].horizon = 0.5f;
			Back[TotalBack++].scroll = false;
		}

		// 나무 8개를 X좌표 0~1599 사이에 랜덤으로 생성시킨다. Y좌표는 고정.
		for ( i = 0; i < 8; i++ )
		{
			Back[TotalBack].SetObject( Pattern[1], 1, 0, MyRand.nextInt(1600), 240, 1, MyRand.nextInt(3) );
			Back[TotalBack++].scroll = false;
		}
		// 플레이어1의 셋팅
		Player1[0].SetObject( Pattern[1], 0, 0, -1000, 367, 2, 0 );
		Player1[1].SetObject( Pattern[1], 0, 0, -1000, Player1[0].y-52, 2, 1 );
		Player1[1].angle = 90;

		// 플레이어2의 셋팅
		Player2[0].SetObject( Pattern[1], 0, 0, 1000, 367, 3, 0 );
		Player2[1].SetObject( Pattern[1], 0, 0, 1000, Player2[0].y-52, 3, 1 );
		Player2[1].angle = -90;
		
		// 카메라 위치를 기본값인 (0,0)에서 좌측으로 -400만큼 이동시킨다.
		gInfo.ScrollX = -400;
	}
	
	public void PushButton( boolean flag )
    {
		if ( flag )
		{
			if ( !TouchFlag )
			{
				// 터치시 시작점과 끝점을 기록한다.
				StartX = TargetX = TouchX;
				StartY = TargetY = TouchY;
				TouchFlag = true;
			}
			else
			{
				// 시작점이 기록된 이후에 들어오는 값은 끝점으로 기록한다.
				TargetX = TouchX;
				TargetY = TouchY;
			}
		}
		else
		{
			// 터치를 뗐을때 화살을 생성한다.
			MakeArrow( Player1[1].x, Player1[1].y, Player1[1].angle );
			TouchFlag = false;
		}
    }
	
	public void MakeArrow( float x, float y, float angle )
	{
		// 새로운 화살이 생성될때 기존에 생성된 점선 오브젝트(type=1)를 모두 삭제한다.
		for ( int k = 0; k < MAX_ARROW; k++ ) if ( Arrow[k].type == 1 ) Arrow[k].dead = true;
		
		for ( int i = 0; i < MAX_ARROW; i++ )
		{
			if ( Arrow[i].dead ) // 죽어있는 오브젝트 검색
			{
				gInfo.PlaySound( SndBuff[0] ); // 화살이 나가는 소리를 플레이한다.
				Arrow[i].SetObject( Pattern[1], 0, 0, x, y, 4, 0 ); // 화살 오브젝트 셋팅
				
				// 거리구하는 공식에 따라 시작점과 끝점 사이의 거리를 구하고, 4를 곱해 적당한 파워를 얻는다.
				float power = (float)Math.sqrt( ((int)Math.abs(StartX-TargetX) ^ 2) + ((int)Math.abs(StartY-TargetY) ^ 2) ) * 4;
				
				// 파워는 100을 넘지 않도록 한다.
				if ( power >= 100 ) power = 100;
				
				// 화살이 날아갈 스피드의 기본값 40에서 파워값에 따라 스피드 값이 줄어들게 한다. 
				Arrow[i].speed = 40.0f * (power / 100);
				Arrow[i].angle = Player1[1].angle; // 플레이어1번의 angle값으로 화살의 각도를 설정한다.
				
				// SinTBL2,CosTBL2는 3600개의 배열개수를 가지고 있으므로 각도값에 10을 곱해 소수점을 없애고 3600도 단위로 변경한다.
				// 변경한 값에 speed를 곱해 화살이 이동할 가로,세로 이동값을 미리 계산해둔다.  
				Arrow[i].horizon  = gInfo.SinTBL2[(int)(Arrow[i].angle * 10)] * Arrow[i].speed;
				Arrow[i].vertical = gInfo.CosTBL2[(int)(Arrow[i].angle * 10)] * Arrow[i].speed;
				
				// 지정된 각도로 화살을 40도트 이동시켜 실제 이미지와 같은 위치로 화살 생성 위치를 보정한다.
				Arrow[i].MovebyAngle( gInfo, Arrow[i].angle, 40 );
				Arrow[i].dead = false;
				return;
			}
		}
	}
	
	public void MakePoint( float x, float y ) // 점 오브젝트를 생성한다. 
	{
		for ( int i = 0; i < MAX_ARROW; i++ )
		{
			if ( Arrow[i].dead )
			{
				Arrow[i].SetObject( Pattern[1], 1, 0, x, y, 5, 0 );
				Arrow[i].dead = false;
				return;
			}
		}
	}
	
	public void MakeBlood( float x, float y, float angle, float power ) // 피 이펙트를 생성한다.
	{
		for ( int i = 0; i < MAX_ARROW; i++ )
		{
			if ( Arrow[i].dead )
			{
				angle += 180;
				if ( angle >= 360 ) angle -= 360; // 넘겨받은 각도가 360도 보다 크면 360도 단위로 보정한다.
				
				Arrow[i].SetObject( Pattern[1], 2, 0, x, y, 6, 0 );
				Arrow[i].speed = power;
				Arrow[i].horizon  = gInfo.SinTBL2[(int)(angle * 10)] * Arrow[i].speed;
				Arrow[i].vertical = gInfo.CosTBL2[(int)(angle * 10)] * Arrow[i].speed;
				Arrow[i].scroll = true;
				Arrow[i].dead = false;
				return;
			}
		}
	}
	
    private void UpdateGame()
    {
    	int i;
    	
    	for ( i = 0; i < TotalBack; i++ )
    	{
    		if ( Back[i].type == 0 ) // 뒷 산 배경
    		{
    			// 카메라 값을 산 이미지의 가로 길이인 748로 나누고 남은 값을 오브젝트의 생성시 X 값인 ox에서 빼서 절대좌표로 변환한다.
    			Back[i].x = Back[i].ox - ((gInfo.ScrollX * Back[i].horizon) % 748);
    			
    			// 절대좌표로 변환한 오브젝트의 위치가 화면 좌측 바깥으로 나가면 오른쪽 끝으로 이동시켜준다.
    			if ( Back[i].x <= -748 ) Back[i].x += 2244;
    			
    			// 절대좌표로 변환한 오브젝트의 위치가 화면 우측 바깥으로 나가면 왼쪽 끝으로 이동시켜준다.
    			if ( Back[i].x >= 800 ) Back[i].x -= 2244;
    		}
    		else if ( Back[i].type == 1 ) // 나무  배경
    		{
    			// 카메라 값을 나무 이미지들이 생성될때 사용된 랜덤값의 범위인 1600으로 나누고 남은 값을 ox에서 빼서 절대좌표로 변환한다.
    			Back[i].x = Back[i].ox - (gInfo.ScrollX % 1600);
    			
    			// 절대좌표로 변환한 오브젝트의 위치가 화면 좌측 바깥으로 나가면 오른쪽 끝으로 이동시켜준다.
    			if ( Back[i].x <= -400 ) Back[i].x += 1600;
    			
    			// 절대좌표로 변환한 오브젝트의 위치가 화면 우측 바깥으로 나가면 왼쪽 끝으로 이동시켜준다.
    			if ( Back[i].x >= 1200 ) Back[i].x -= 1600;
    		}
    		// Back[] 오브젝트들은 scroll값이 false로 셋팅되어 절대 좌표를 사용하는 오브젝트로 지정돼 있다.
    		// 그러나 카메라의 Y 좌표가 화살을 따라가다 보면 화면 위쪽을 보게되는 경우가 생기기 때문에 Y 좌표에서 ScrollY를 빼고 상대 좌표로 변환한다.
    		// X 좌표는 화면 상의 좌표인 절대 좌표를 사용하고, Y 좌표는 상대 좌표를 사용하게 된다. 
    		Back[i].y = Back[i].oy - gInfo.ScrollY; 
    	}
    	// 카메라가 화살을 따라가야 하는지, 플레이어를 따라가야 하는지 결정하기 위한 변수
    	boolean camera = false;
    	
    	for ( i = 0; i < MAX_ARROW; i++ )
    	{
    		if ( !Arrow[i].dead )
    		{
    			if ( Arrow[i].type == 0 ) // 화살 오브젝트의 처리
    			{
    				if ( Arrow[i].damage == 0 ) // damage 변수가 0일때 (디폴트)
    				{
    					MakePoint( Arrow[i].x, Arrow[i].y ); // 이동하는 동안 점 오브젝트를 생성한다.
    					
    					// 화살 생성시 계산해 두었던 horizon, vertical 값을 X 좌표에 더해주고 Y 좌표에는 빼줘서 지정 각도로 이동시킨다. 
    					Arrow[i].x += Arrow[i].horizon;
    					Arrow[i].y -= Arrow[i].vertical;
    					
    					// Y 좌표의 이동 각도를 0.5f씩 감소시켜서 포물선을 그리도록 한다.
    					// vertical에서 빼는 값이 작으면 작을수록 적용되는 중력이 낮아진다.
    					Arrow[i].vertical -= 0.5f;
    					
    					// 최초 위치를 나타내는 ox,oy에서 이동한 신규 좌표 x,y까지의 각도를 구해 angle에 넣는다.
    					// 날아가는 방향으로 화살이 회전하게 된다.
	    				Arrow[i].angle = (float)gInfo.GetAngle( Arrow[i].ox, Arrow[i].oy, Arrow[i].x, Arrow[i].y );
	    				
	    				// ox,oy를 신규 좌표로 다시 설정한다.
	    				Arrow[i].ox = Arrow[i].x;
	    				Arrow[i].oy = Arrow[i].y;
	    				
	    				// 플레이어2와 화살이 충돌한 경우
	    				if ( Player2[0].CheckPos((int)Arrow[i].x, (int)Arrow[i].y) )
	    				{
	    					gInfo.PlaySound( SndBuff[1] );
	    					
	    					// 화살의 X 좌표를 플레이어2의 좌표로 설정한다. 화살이 박힌 것으로 표현하기 위함.
	    					Arrow[i].x = Player2[0].x;
	    					Arrow[i].damage = 1; // damage 값을 1로 설정해서 다른 루틴로 분기한다.
	    					Arrow[i].timer = System.currentTimeMillis() + 1000; // 플레이어2에 화살이 충돌한 순간부터 1초간 지연하기 위한 값
	    				}
	    				else if ( Arrow[i].y >= 367 ) // 화살이 플레이어2에 맞지 않고 땅에 박힌 경우
	    				{
	    					gInfo.PlaySound( SndBuff[2] );
	    					Arrow[i].damage = 2; // damage 값을 2로 설정해서 다른 루틴으로 분기한다.
	    					Arrow[i].timer = System.currentTimeMillis() + 1000; // 땅에 화살이 박히고 1초간 지연하기 위한 값
	    				}
    				}
    				else if ( Arrow[i].damage == 1 || Arrow[i].damage == 2 ) // 상대방에게 박혔거나 땅에 박힌 경우
    				{
    					if ( Arrow[i].damage == 1 ) // 땅에 박혔으면
    					{
    						// 랜덤하게 피를 생성한다.
    						MakeBlood( Arrow[i].x, Arrow[i].y, Arrow[i].angle+(MyRand.nextInt(11)-5)*2, Arrow[i].speed / (5+MyRand.nextInt(10)*0.5f) ); 
    					}
    					// 박힌 이후 1초가 지났으면 damage 값을 3으로 설정한다. 화살은 사라지지 않지만 아무 처리도 하지 않게 된다.
    					if ( System.currentTimeMillis() >= Arrow[i].timer ) Arrow[i].damage = 3;
    				}
    				
    				if ( Arrow[i].damage != 3 ) // 화살이 죽은 상태가 아니면
    				{
	    				camera = true;
	    				gInfo.MoveCamera( Arrow[i].x-400, Arrow[i].y-367, Arrow[i].speed ); // 카메라가 화살을 따라가게 한다.        				
    				}    				
    			}
    			
    			if ( Arrow[i].type == 2 ) // 피 오브제트 (파이클)
    			{
    				if ( Arrow[i].damage == 0 )
    				{
    					// 기본적으로 화살과 같은 루틴을 사용한다. 화살이 박힌 각도에서 180도 이동한 각도로 피가 뿌려진다.
    					Arrow[i].x += Arrow[i].horizon;
    					Arrow[i].y -= Arrow[i].vertical;
    					Arrow[i].vertical -= 0.2f; // 중력은 0.2f로 작게 적용시킨다.
    					
    					Arrow[i].Zoom( gInfo, 0.03f, 0 ); // 피의 가로 스케일을 약간씩 늘려준다.
    					
	    				if ( Arrow[i].y >= 367 ) // 피가 땅에 닿으면
	    				{
	    					Arrow[i].damage = 1;
	    					Arrow[i].timer = System.currentTimeMillis() + 1000; // 피를 1초간 보여준다.
	    				}
    				}
    				else if ( Arrow[i].damage == 1 ) // 땅에 피가 떨어지고 1초가 지나면 삭제시킨다.
    				{
    					if ( System.currentTimeMillis() >= Arrow[i].timer ) Arrow[i].dead = true;
    				}
    			}
    		}
    	}
    	if ( !camera ) gInfo.MoveCamera( Player1[0].x-400, Player1[0].y-367, 20 ); // 화살이 없는 경우 플레이어1을 카메라가 따라가도록 한다.
    	
    	if ( gInfo.ScrollY > 0 ) gInfo.ScrollY = 0; // 카메라의 Y 좌표가 0 이상 넘어가지 않도록 한다. 땅 아래쪽이 보이지 않도록 하기 위함.
    }
    
    public void DrawAngle() // 시작점부터 끝점까지 점선을 그려주는 메소드
    {
    	float x = StartX, y = StartY;
    	int angle = (int)(gInfo.GetAngle( StartX, StartY, TargetX, TargetY ) * 10); // 시작점에서 끝점까지의 각도를 구해 3600도 단위로 변경한다.
    	
    	while ( true )
    	{
    		Pattern[1].PutAniReal( gInfo, (int)x, (int)y, 5, 0 ); // 점을 그려준다.

    		// 계산된 각도로 20도트씩 이동시킨다. 
    		x += gInfo.SinTBL2[angle] * 20;
    		y -= gInfo.CosTBL2[angle] * 20;

    		// StartX,StartY에서 TargetX,TargetY에 도달하면 그리기를 끝낸다.
    		if ( TargetX > StartX )
    		{
    			if ( x >= TargetX ) return;
    		}
    		else
    		{
    			if ( x <= TargetX ) return;
    		}

    		if ( TargetY > StartY )
    		{
    			if ( y >= TargetY ) return;
    		}
    		else
    		{
    			if ( y <= TargetY ) return;
    		}
    	}
    }
    Font font = new Font(); // 폰트 객체 생성

    public void DoGame()
    {
    	int i;
    	
    	UpdateGame();

    	font.BeginFont();
		Pattern[0].PutAniReal( gInfo, 400, 240, 0, 0 ); // 하늘 배경 그리기
		
		for ( i = 0; i < TotalBack; i++ ) Back[i].DrawSprite( gInfo ); // 산과 나무 그리기		
		for ( i = 0; i < 2; i++ )
		{
			Player1[i].DrawSprite( gInfo ); // 플레이어1의 몸통과 팔 그리기
			Player2[i].DrawSprite( gInfo ); // 플레이어2의 몸통과 팔 그리기
		}
		Pattern[0].PutAniReal( gInfo, 400, (int)(240-gInfo.ScrollY), 1, 0 ); // 땅 그리기
		
		for ( i = 0; i < MAX_ARROW; i++ ) if ( !Arrow[i].dead ) Arrow[i].DrawSprite( gInfo ); // 화살,피,점 그리기
		
    	if ( TouchFlag )
    	{
        	Player1[1].angle = (float)gInfo.GetAngle( TargetX, TargetY, StartX, StartY );
    		//gInfo.DrawLine( gl, (int)TargetX, (int)TargetY, (int)StartX, (int)StartY, 0, 0, 0, 1.0f, 2.0f ); // 유료 버전 이용자는 DrawAngle() 대신 이 메소드를 호출한다.
        	DrawAngle(); // 점선을 그려준다.
    	}
    	// 시작점과 끝점의 거리를 계산해서 화면에 출력해준다.
    	float power = (float)Math.sqrt( ((int)Math.abs(StartX-TargetX) ^ 2) + ((int)Math.abs(StartY-TargetY) ^ 2) );
    	font.DrawColorFont( gl, 10, 10, 0, 0, 0, 24, String.format("%f", power) );
    	font.EndFont();
    }
}
